#include "mainwindow.h"
#include "ui_mainwindow.h"

#define len 4

static int person = 0;;
static int ID = 0;
static int Num_flag = 0;
static int Num[len] = {0};
static QString show_num = ""; // 初始化静态字符串
const int password = 4236;

#define X86 0
#define ARM 1

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

#if ARM
  cap.open("/dev/video9");
#endif

#if X86
    cap.open(0);
#endif

//  seeta init code begin
    seeta::ModelSetting::Device device = seeta::ModelSetting::CPU;
    int id = 0;
    // 人脸检测模块加载
#if X86
    seeta::ModelSetting FD_model( "/mnt/hgfs/mnt/model/model/fd_2_00.dat", device, id );
    // 关键点检测模块加载
    seeta::ModelSetting PD_model( "/mnt/hgfs/mnt/model/model/pd_2_00_pts5.dat", device, id );
    // 特征点提取与比对模块加载
    seeta::ModelSetting FR_model( "/mnt/hgfs/mnt/model/model/fr_2_10.dat", device, id );

#endif
#if ARM
    seeta::ModelSetting FD_model( "./fd_2_00.dat", device, id );
    // 关键点检测模块加载
    seeta::ModelSetting PD_model( "./pd_2_00_pts5.dat", device, id );
    // 特征点提取与比对模块加载
    seeta::ModelSetting FR_model( "./fr_2_10.dat", device, id );
#endif

    // 初始化
    FD = new seeta::FaceTracker(FD_model);
    engine = new seeta::FaceEngine( FD_model, PD_model, FR_model, 2, 16 );
    seeta::FaceRecognizer  mRecognizer(FR_model);

    FD->set(seeta::FaceTracker::PROPERTY_VIDEO_STABLE,1);
    // set face detector's min face size
    engine->FD.set( seeta::FaceDetector::PROPERTY_MIN_FACE_SIZE, 80 );
    // 容器（ 保存图片路径名 ）
#if X86
    std::vector<std::string> GalleryImageFilename = { "./kunkun.bmp" };
#endif
#if ARM
    std::vector<std::string> GalleryImageFilename = { "./yes.bmp" };
#endif
    // 容器（ 保存索引号 ）
    std::vector<int64_t> GalleryIndex( GalleryImageFilename.size() );
    // i < 图片路径名容器元素数量
    for( size_t i = 0; i < GalleryImageFilename.size(); ++i )
    {
        // register face into facedatabase std::string &filename （ 引用图片路径名容器 ）
        std::string &filename = GalleryImageFilename[i];

        //  int64_t &index = GalleryIndex[i]; （ 引用索引号容器 ）
        int64_t &index = GalleryIndex[i];

        std::cerr << "Registering... " << filename << std::endl; // 打印输出

        // 加载（ 读取图片 ）
        seeta::cv::ImageData image = cv::imread( filename );

        // 注册人脸
        auto id = engine->Register( image );

        // id == -1 表示注册失败（ 成功返回索引号 ）
        index = id;
        std::cerr << "Registered id = " << id << std::endl;     // 打印输出
    }
    // i < 索引号容器数量
    for( size_t i = 0; i < GalleryIndex.size(); ++i )
    {
        // save index and name pair
        // 判断索引号是否小于0（ -1注册失败，其他成功 ）
        if( GalleryIndex[i] < 0 ) continue;

        // 保存索引号，图片路径名
        GalleryIndexMap.insert( std::make_pair( GalleryIndex[i], GalleryImageFilename[i] ) );
    }
    ui->pushButton_3->close();

//  seeta init code end

}

MainWindow::~MainWindow()
{
    delete ui;
}

std::string Extract_name(std::string filename)
{
    // 取出人名
    size_t last_slash_pos = filename.rfind('/');  // 找到最后一个'/'的位置
    if (last_slash_pos != std::string::npos) // 如果找到了'/'，则截取文件名（包括后缀）
    {
        filename = filename.substr(last_slash_pos + 1);
    }
    size_t last_dot_pos = filename.rfind('.'); // 找到文件名中第一个'.'的位置
    if (last_dot_pos != std::string::npos)   // 如果找到了'.'，则截取文件名（不包括后缀）
    {
        filename = filename.substr(0, last_dot_pos);
    }
    return filename;
}

void MainWindow::timerEvent(QTimerEvent *event)
{
    Show_cap_image(0);
}


void MainWindow::on_pushButton_2_clicked()
{
//    Show_cap_image(1);
    ui->textEdit->setText("正在检测...");
    // 如果摄像头已打开
    if( cap.isOpened() )
    {
        // 取出一帧数据
        cap >> image;

        // 取出数据帧为空，则跳过此次循环
        if( image.empty() ) return;

        // 定义 image 保存 frame
        seeta::cv::ImageData image_2 = image;

        // Detect all faces
        // 容器（ 检测到的人脸 ）
        // 人脸检测
        std::vector<SeetaFaceInfo> faces = engine->DetectFaces( image_2 );
        if (faces.empty())
        {
            ui->textEdit->setText("没有检测到人脸!");
        }
        // 如果有人脸
        for( SeetaFaceInfo &face : faces )
        {
            // Query top 1
            int64_t index = -1;
            float similarity = 0;   // 相似度

            //检测人脸5个特征点
            auto points = engine->DetectPoints(image_2, face);

            //在图片上把人脸框出来
            cv::rectangle( image, cv::Rect( face.pos.x, face.pos.y, face.pos.width, face.pos.height ), CV_RGB( 128, 128, 255 ), 3 );

            //把5个特征点绘制出来
            for (int i = 0; i < 5; ++i)
            {
                auto &point = points[i];
                cv::circle(image, cv::Point(int(point.x), int(point.y)), 2, CV_RGB(128, 255, 128), -1);
            }

            // 字符类
            std::string name;

            // 人脸质量评估
            auto score = QA.evaluate(image_2, face.pos, points.data());
            std::cout<<score<<std::endl;         // 数据越大人脸质量越好

            if (score == 0)                      // 人脸质量不行，（ 没有脸 ）
            {
                name = "ignored";
            }
 #if 1
            if(1) {
                // 对比相似度
                auto queried = engine->QueryTop( image_2, points.data(), 1, &index, &similarity );

                // no face queried from database 表示没有查询到人脸
                if (queried < 1) return;
                // similarity greater than threshold, means recognized
                if( similarity > threshold ) // 判断相似度是否达到阈值（ 0.7 ）
                {
                    // name 赋值为  图片路径名（ 下标引用方法 ）
                    name = GalleryIndexMap[index];
                    qDebug() << "yes!" << endl;
                }
                else
                {
                    qDebug() << "no!" << endl;
                }
            }
  #endif
            // name 不为空
            if( !name.empty() )
            {
                // 绘制图片名
                name = Extract_name(name);
                cv::putText( image, name, cv::Point( face.pos.x, face.pos.y - 5 ), 3, 1, CV_RGB( 255, 128, 128 ) );
            }
            else
            {
                name = Extract_name(name);
            }
            /*show name*/
            QString name_data = {"Name:"};
            name_data.append(QString::fromStdString(name));
            ui->textEdit->setText(name_data);
            if(similarity < threshold)
            {
                ui->textEdit->setText("no!");
            }
        }
    }
    /*show picture*/
    QImage img(image.data,image.cols,image.rows,QImage::Format_BGR888);
    QPixmap mmp = QPixmap::fromImage(img);
    mmp = mmp.scaled(ui->label_2->size());
    ui->label_2->setPixmap(mmp);
}

void MainWindow::on_pushButton_clicked()
{
    ui->label->show();    
    if(ID == 0)
    {
        ID = startTimer(100);
        ui->pushButton->setText("停止");
    }
    else
    {
        killTimer(ID);
        ID = 0;
        ui->pushButton->setText("开始");
        ui->label->clear();
    }
}

void MainWindow::Show_cap_image(int mode)
{
    // auto video_width = capture.get(cv::CAP_PROP_FRAME_WIDTH);
    // auto video_height = capture.get(cv::CAP_PROP_FRAME_HEIGHT);
    cap.grab();
    cap.retrieve(image);

    if (image.empty()) qDebug() << "null err !" << endl;

    seeta::cv::ImageData simage = image;

    auto faces = FD->track(simage);

    for (int i = 0; i < faces.size; ++i) {
        auto &face = faces.data[i];
        cv::rectangle(image, cv::Rect(face.pos.x, face.pos.y, face.pos.width, face.pos.height),
                      CV_RGB(128, 128, 255), 3);
        cv::putText(image, std::to_string(face.PID), cv::Point(face.pos.x, face.pos.y - 5), 3, 1,
                    CV_RGB(255, 128, 128));
    }
    QImage img(image.data,image.cols,image.rows,QImage::Format_BGR888);
    QPixmap mmp = QPixmap::fromImage(img);

    if(mode == 1)
    {
        mmp = mmp.scaled(ui->label_2->size());
        ui->label_2->setPixmap(mmp);
    }
    else if (mode == 0)
    {
        mmp = mmp.scaled(ui->label->size());
        ui->label->setPixmap(mmp);
    }


}

/*录入人脸功能*/
void MainWindow::on_pushButton_3_clicked()
{
    cap >> image;
    // 定义 image 保存 frame
    seeta::cv::ImageData image_2 = image;
    // Detect all faces
    // 容器（ 检测到的人脸 ）
    // 人脸检测
    std::vector<SeetaFaceInfo> faces = engine->DetectFaces( image_2 );

    // 如果有人脸
    bool faceDetected = false; // 初始化标志变量

    for (SeetaFaceInfo &face : faces)
    {
        faceDetected = true; // 检测到人脸，设置标志为true
        bool isSuccess = cv::imwrite("yes.bmp", image);

        if (isSuccess)
        {
            ui->textEdit->setText("保存成功"); // 保存成功
            break; // 如果保存成功，可以跳出循环
        }
        else
        {
            ui->textEdit->setText("保存失败"); // 保存失败
            break; // 如果保存失败，也可以跳出循环
        }
    }
    if (!faceDetected) // 如果没有检测到人脸
    {
        ui->textEdit->setText("保存失败"); // 设置文本为“保存失败”
    }

    // 容器（ 保存图片路径名 ）
#if X86
    std::vector<std::string> GalleryImageFilename = { "./kunkun.bmp" };
#endif
#if ARM
    std::vector<std::string> GalleryImageFilename = { "./yes.bmp" };
#endif
    // 容器（ 保存索引号 ）
    std::vector<int64_t> GalleryIndex( GalleryImageFilename.size() );
    // i < 图片路径名容器元素数量
    for( size_t i = 0; i < GalleryImageFilename.size(); ++i )
    {
        // register face into facedatabase std::string &filename （ 引用图片路径名容器 ）
        std::string &filename = GalleryImageFilename[i];

        //  int64_t &index = GalleryIndex[i]; （ 引用索引号容器 ）
        int64_t &index = GalleryIndex[i];

        std::cerr << "Registering... " << filename << std::endl; // 打印输出

        // 加载（ 读取图片 ）
        seeta::cv::ImageData image = cv::imread( filename );

        // 注册人脸
        auto id = engine->Register( image );

        // id == -1 表示注册失败（ 成功返回索引号 ）
        index = id;
        std::cerr << "Registered id = " << id << std::endl;     // 打印输出
    }
    // i < 索引号容器数量
    for( size_t i = 0; i < GalleryIndex.size(); ++i )
    {
        // save index and name pair
        // 判断索引号是否小于0（ -1注册失败，其他成功 ）
        if( GalleryIndex[i] < 0 ) continue;

        // 保存索引号，图片路径名
        GalleryIndexMap.insert( std::make_pair( GalleryIndex[i], GalleryImageFilename[i] ) );
    }
}

/*num key */
void MainWindow::show_live_num(int operate)
{
    if(operate == 1)
    {
        if(Num_flag>=0 && Num_flag <= 3)
        {
            if(show_num.size() >= len)
            {
                show_num.clear();
            }
            show_num.append(QString::number(Num[Num_flag]));
        }

    }
    else if(operate == 0)
    {
        if(!QString(show_num).isEmpty())
        {
            show_num.chop(1);
        }
    }
    ui->textEdit->setText(show_num);
}

void MainWindow::on_pushButton_4_clicked()
{
    Num[Num_flag] = 1;
    show_live_num(1);
    Num_flag++;
    if(Num_flag >= len)
    {
        Num_flag = len - 1;
    }

}

void MainWindow::on_pushButton_6_clicked()
{
    Num[Num_flag] = 2;
    show_live_num(1);
    Num_flag++;
    if(Num_flag >= len)
    {
        Num_flag = len - 1;
    }

}

void MainWindow::on_pushButton_7_clicked()
{
    Num[Num_flag] = 3;
    show_live_num(1);
    Num_flag++;
    if(Num_flag >= len)
    {
        Num_flag = len - 1 ;
    }

}

void MainWindow::on_pushButton_5_clicked()
{
    Num[Num_flag] = 4;
    show_live_num(1);
    Num_flag++;
    if(Num_flag >= len)
    {
        Num_flag = len - 1;
    }
}

void MainWindow::on_pushButton_8_clicked()
{
    Num[Num_flag] = 5;
    show_live_num(1);
    Num_flag++;
    if(Num_flag >= len)
    {
        Num_flag = len - 1;
    }
}

void MainWindow::on_pushButton_9_clicked()
{
    Num[Num_flag] = 6;
    show_live_num(1);
    Num_flag++;
    if(Num_flag >= len)
    {
        Num_flag = len - 1;
    }
}

void MainWindow::on_pushButton_10_clicked()
{
    Num[Num_flag] = 7;
    show_live_num(1);
    Num_flag++;
    if(Num_flag >= len)
    {
        Num_flag = len - 1;
    }
}

void MainWindow::on_pushButton_11_clicked()
{
    Num[Num_flag] = 8;
    show_live_num(1);
    Num_flag++;
    if(Num_flag >= len)
    {
        Num_flag = len - 1;
    }
}

void MainWindow::on_pushButton_12_clicked()
{
    Num[Num_flag] = 9;
    show_live_num(1);
    Num_flag++;
    if(Num_flag >= len)
    {
        Num_flag = len - 1;
    }
}

void MainWindow::on_pushButton_13_clicked()
{
    Num[Num_flag] = 0;
    show_live_num(1);
    Num_flag++;
    if(Num_flag >= len)
    {
        Num_flag = len - 1;
    }
}

void MainWindow::on_pushButton_15_clicked()
{
    if(Num_flag <= 0)
    {
        Num_flag = 0;
        show_live_num(0);
    }
    else
    {
        Num_flag--;
        show_live_num(0);
    }

}
/*Enter key */
void MainWindow::on_pushButton_14_clicked()
{
    if((Num[0] == (password/1000)) &&
       (Num[1] == ((password % 1000)/100)) &&
       (Num[2] == ((password % 100)/10)) &&
       (Num[3] == (password % 10)) )
    {
        ui->textEdit->setText("密码正确！");
        ui->pushButton_3->show();
        show_num.clear();
    }
    else
    {
        ui->textEdit->setText("密码错误！");
        Num_flag = 0;
        ui->pushButton_3->close();
        show_num.clear();
    }
}

